/*
 * SimpleTre.java
 *
 * Copyright(c) 2010
 * Michael Kroez
 * kroez@studiorutton.de
 *
 * All rights reserved.
 *
 * $Id$
 * 
 */

package de.studiorutton.tre;

import java.util.ArrayList;
import java.util.regex.Pattern;

import de.studiorutton.gerengdic.databaseaccess.ContentAccess;


public class SimpleTre {
	private static String TAG = "libtre";
	public final static int MATCH_FOUND = 0;
	public final static int MATCH_NOTFOUND = -1;

	private final static int REGEX_EFLAGS = 0;
	private final static long REGEX_NMATCH = 1;

	public final static int FULL_WORDS = 0;
	public final static int FULL_BEGINNING = 1;
	public final static int NO_FULL_WORDS = 2;

	private regmatch_t pMatch = new regmatch_t();
	private regamatch_t aMatch = new regamatch_t();
	private ArrayList<RegexBundle> regexList = new ArrayList<RegexBundle>();

	private Pattern doubleCollons = Pattern.compile("::");

	class RegexBundle {
		public regex_t regex;
		public regaparams_t aparams;
	}

	public static boolean loadLib() {
		// Load JNI library
		try {
			System.loadLibrary("tre");
		} catch (UnsatisfiedLinkError x) {
//			Log.i(TAG, "Link error with tre: " + x.getMessage());
			return false;
		}

//		Log.i(TAG, "Successfully loaded libtre");
		LIBTRE.setlocaleToNothing();
		return true;
	}

	public final static native int tre_getconfigvalue(long jarg2);

	public static int tre_getconfigvalue(SWIGTYPE_p_void result) {
		return tre_getconfigvalue(SWIGTYPE_p_void.getCPtr(result));
	}

	public boolean hasMultibyteSupport() {
		SWIGTYPE_p_void result = LIBTRE.new_int_as_void_p();
		int status = LIBTRE.tre_config(LIBTREConstants.TRE_MULTIBYTE, result);
		int hasMultibyte = LIBTRE.void_p_as_int(result);
		LIBTRE.free_void_p(result);
		if (hasMultibyte == 1)
			return true;
		else
			return false;
	}

	public int getRegexListSize() {
		return regexList.size();
	}

	public int compileRegex(String s, int fullWords, boolean partial,
			boolean caseInsensitive, boolean aMatchEnabled) throws TreException {
		regexList.clear();
		return addCompileRegex(s, fullWords, partial, caseInsensitive,
				aMatchEnabled);
	}

	public int multiCompileRegex(String s, int fullWords, boolean partial,
			boolean caseInsensitive, boolean aMatchEnabled) throws TreException {
		String sParts[] = s.split(" +");
		StringBuilder sb = new StringBuilder();
		for (String sSingle : sParts) {
			sb.append("*" + sSingle + "* ");
		}

//		Log.d(TAG, sb.toString());

		regexList.clear();
		int result = 0;
		for (String sSingle : sParts) {
			result = addCompileRegex(sSingle, fullWords, partial,
					caseInsensitive, aMatchEnabled);
		}
		return result;
	}

	public String searchStringFixes(String s) {
		// Hacks, für spezielle Situationen
		s = s.replace("(", "\\(").replace(")", "\\)").replace(".", "\\.");
		s = s.replace("ss", "(ss|ß){-0}");

		s = s.replace("ae", "(ae|ä){-0}").replace("oe", "(oe|ö){-0}")
				.replace("ue", "(ue|ü)");
		s = s.replace("Ae", "(Ae|Ä){-0}").replace("Oe", "(Oe|Ö){-0}")
				.replace("Ue", "(Ue|ü){-0}");

		return s;
	}

	public int addCompileRegex(String s, int fullWords, boolean partial,
			boolean caseInsensitive, boolean aMatchEnabled) throws TreException {
		int cflags = LIBTREConstants.REG_EXTENDED;
		String rawSearchString = s;

		if (caseInsensitive) {
			cflags |= LIBTREConstants.REG_ICASE;
		}

		s = searchStringFixes(s);

		if (s.substring(0, 1).equals("'"))
			fullWords = NO_FULL_WORDS;

		int status = 0;
		regex_t regex = new regex_t();
		StringBuilder regexString = new StringBuilder();
		if (fullWords == FULL_WORDS)
			regexString.append("\\<(" + s + ")\\>");
		else if (fullWords == NO_FULL_WORDS)
			regexString.append("(" + s + ")");
		else if (fullWords == FULL_BEGINNING)
			regexString.append("\\<(" + s + ")");

		if (partial)
			regexString.append("(.*)");

		if ((status = LIBTRE.tre_regcomp(regex, regexString.toString(), cflags)) != reg_errcode_t.REG_OK
				.swigValue())
			throw new TreException("Error in regex: "
					+ reg_errcode_t.swigToEnum(status).toString() + ".");

		RegexBundle rb = new RegexBundle();
		rb.regex = regex;
		if (aMatchEnabled)
			if (rawSearchString.length() < 4)
				rb.aparams = SimpleTreAMatchingParams.strictParams();
			else if (rawSearchString.length() >= 4
					&& rawSearchString.length() < 9)
				rb.aparams = SimpleTreAMatchingParams.midParams();
			else
				rb.aparams = SimpleTreAMatchingParams.lazyParams();
		else
			rb.aparams = SimpleTreAMatchingParams.strictParams();

		regexList.add(rb);
		return status;
	}

	// public void createPMatchBuffer() {
	// pMatch = new regmatch_t();
	// }

	public int matchAllRegex(String s, int language,
			AMatchResult leftRightResult[]) throws TreException {
		int result;
		AMatchResult minLeftResult = null, minRightResult = null;

		for (RegexBundle regexBundle : regexList) {
			result = matchRegex(s, regexBundle, language, leftRightResult);
			if (result == MATCH_NOTFOUND)
				return MATCH_NOTFOUND;
			else {
				if (leftRightResult[0].smallerThan(minLeftResult))
					minLeftResult = leftRightResult[0];

				if (leftRightResult[0].smallerThan(minRightResult))
					minRightResult = leftRightResult[1];
			}
		}

		leftRightResult[0] = minLeftResult;
		leftRightResult[1] = minRightResult;

		return MATCH_FOUND;
	}

	public int matchRegex(String s, RegexBundle regexBundle, int language,
			AMatchResult matchResults[]) throws TreException {

		int leftStatus = MATCH_NOTFOUND;
		int rightStatus = MATCH_NOTFOUND;

		int doubleCollonIndex = s.indexOf("::");

		String leftString = null, rightString = null;
		if (doubleCollonIndex > 0) {
			leftString = s.substring(0, doubleCollonIndex - 1);
			rightString = s.substring(doubleCollonIndex + 2, s.length());
		} else {
			leftString = s;
		}

		if (language == ContentAccess.LANGUAGE_LEFT
				|| language == ContentAccess.LANGUAGE_BOTH)
			leftStatus = matchRegexRaw(leftString, regexBundle, matchResults[0]);
		if ((rightString != null)
				&& (language == ContentAccess.LANGUAGE_RIGHT || language == ContentAccess.LANGUAGE_BOTH)) {
			rightStatus = matchRegexRaw(rightString, regexBundle,
					matchResults[1]);
		}
		if (leftStatus == MATCH_NOTFOUND && rightStatus == MATCH_NOTFOUND)
			return MATCH_NOTFOUND;
		else
			return MATCH_FOUND;
	}

	public int matchRegexFirst(String s, AMatchResult result)
			throws TreException {
		return matchRegexRaw(s, regexList.get(0), result);
	}

	public int matchRegexRaw(String s, RegexBundle regex, AMatchResult result)
			throws TreException {
		int status;

		aMatch.setPmatch(pMatch);
		aMatch.setNmatch(REGEX_NMATCH);

		status = LIBTRE.tre_regaexec(regex.regex, s, aMatch, regex.aparams,
				REGEX_EFLAGS);
		if (status == reg_errcode_t.REG_NOMATCH.swigValue())
			return MATCH_NOTFOUND;
		else if (status == reg_errcode_t.REG_OK.swigValue()) {
			if (result != null) {
				result.position = pMatch.getRm_so();
				result.end = pMatch.getRm_eo();
				if (regex.aparams == SimpleTreAMatchingParams.strictParams()) {
					result.cost = 0;
					result.errors = 0;
				} else {
					result.cost = aMatch.getCost();
					result.errors = aMatch.getNum_subst() + aMatch.getNum_ins()
							+ aMatch.getNum_del();
				}
			}
			return MATCH_FOUND;
		} else
			throw new TreException("Error in searching: "
					+ reg_errcode_t.swigToEnum(status).toString() + ".");
	}

}
